React源码之组件的实现与首次渲染 | 您所在的位置:网站首页 › react 源码阅读 › React源码之组件的实现与首次渲染 |
![]() react: v15.0.0 本文讲 组件如何编译 以及 ReactDOM.render 的渲染过程。 babel 的编译 babel 将 React JSX 编译成 JavaScript. 在 babel 官网写一段 JSX 代码编译结果如图: 每个标签的创建都调用了 React.createElement. 源码中的两种数据结构 贯穿源码,常见的两种数据结构,有助于快速阅读源码。 ReactElement结构如下: { $$typeof // ReactElement标识符 type // 组件 key ref props // 组件属性和children }是 React.createElement 的返回值。 ReactComponentReactComponent 这个名字有点奇怪。 结构如下: { _currentElement // ReactElement ... // 原型链上的方法 mountComponent, // 组件初次加载调用 updateComponent, // 组件更新调用 unmountComponent, // 组件卸载调用 }是 ReactCompositeComponent 的 instance 类型。其余三种构造函数 ReactDOMComponent、ReactDOMTextComponent、ReactEmptyComponent 的实例结构与其相似。 React.createElement React.createElement 实际执行的是 ReactElement.createElement。 ReactElement.createElement 接收三个参数: type: string | Component config: 标签上的属性 …children: children元素集合重点关注 type 和 props。 然后看 ReactElement 方法,只是做了赋值动作。 综上,我们写的代码编译后是这样的: class C extends React.Component { render() { return { type: "div", props: { children: this.props.value, }, }; } } class App extends React.Component { render() { return { type: "div", props: { children: [ { type: "span", props: { children: "aaapppppp", }, }, "123", { type: C, props: { value: "ccc", }, }, ] }, }; } } ReactDOM.render( { type: App, props: {}, }, document.getElementById("root") );ReactDOM.render 先来看下 ReactDOM.render 源码的执行过程 在 _renderNewRootComponent 方法中,调用了 instantiateReactComponent,生成了的实例结构类似于 ReactComponent。 instantiateReactComponent 的参数是 node,node 的其中一种格式就是 ReactElement。 根据 node & node.type 的类型,会执行不同的方法生成实例 ReactCompositeComponent ReactDOMComponent ReactDOMTextComponent ReactEmptyComponent简化如下 var instantiateReactComponent = function (node) { if (node === null || node === false) { return new ReactEmptyComponent(node); } else if (typeof node === 'object') { if (node.type === 'string') { return new ReactDOMComponent(node); } else { return new ReactCompositeComponent(node); } } else if (typeof node === 'string' || typeof node === 'number') { return new ReactDOMTextComponent(node); } }通过四种方式实例化后的对象基本相似 var instance = { _currentElement: node, _rootNodeID: null, ... } instance.__proto__ = { mountComponent, updateComponent, unmountComponent, }四种 mountComponent 简化如下 ReactCompositeComponent mountComponent: function () { // 创建当前组件的实例 this._instance = new this._currentElement.type(); // 调用组件的 render 方法,得到组件的 renderedElement renderedElement = this._instance.render(); // 调用 instantiateReactComponent, 得到 renderedElement 的实例化 ReactComponent this._renderedComponent = instantiateReactComponent(renderedElement); // 调用 ReactComponent.mountComponent return this._renderedComponent.mountComponent(); } ReactDOMComponentreact 源码中,插入 container 前使用 ownerDocument、DOMLazyTree 创建和存放节点,此处为了方便理解,使用 document.createElement 模拟。 mountComponent: function () { var { type, props } = this._currentElement; var element = document.createElement(type); if (props.children) { var childrenMarkups = props.children.map(function (node) { var instance = instantiateReactComponent(node); return instance.mountComponent(); }) element.appendChild(childrenMarkups) } return element; } ReactDOMTextComponent mountComponent: function () { return this._currentElement; } ReactEmptyComponent mountComponent: function () { return null; }ReactDOM.render 简化 简化如下: ReactDOM.render = function (nextElement, container) { var nextWrappedElement = ReactElement( TopLevelWrapper, null, null, null, null, null, nextElement ); var componentInstance = instantiateReactComponent(nextElement); var markup = componentInstance.mountComponent; container.innerHTML = markup; }总结 babel 将 JSX 语法编译成 React.createElement 形式。 源码中用到了两个重要的数据结构 ReactElement ReactComponent React.createElement 将我们写的组件处理成 ReactElement 结构。 ReactDOM.render 传入 ReactElement 和 container, 渲染流程如下 在 ReactElement 外套一层,生成新的 ReactElement 实例化 ReactElement:var instance = instantiateReactComponent(ReactElement) 递归生成 markup:var markup = instance.mountComponent() 将 markup 插入 container:container.innerHTML = markup 原文地址https://www.cnblogs.com/whosmeya/p/13222478.html |
CopyRight 2018-2019 实验室设备网 版权所有 |